<script lang="tsx" setup>
import icon_upload_outlined from '@/assets/svg/icon_upload_outlined.svg'
import { Icon } from '@/components/icon-custom'
import { ElIcon } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
import {
  ref,
  shallowRef,
  reactive,
  h,
  computed,
  toRefs,
  onMounted,
  onBeforeUnmount,
  nextTick
} from 'vue'
import { fieldType as fieldTypeLowercase } from '@/utils/attr'
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
import { save, update } from '@/api/datasource'
import type { Action } from 'element-plus-secondary'
import { Base64 } from 'js-base64'
import ExcelInfo from '../ExcelInfo.vue'
import SheetTabs from '../SheetTabs.vue'
import { cloneDeep, debounce } from 'lodash-es'
import { uploadFile } from '@/api/datasource'
import { useEmitt } from '@/hooks/web/useEmitt'
import { iconFieldMap } from '@/components/icon-group/field-list'
import { boolean } from 'mathjs'

export interface Param {
  editType: number
  pid?: string
  type?: string
  id?: string
  name?: string
  creator?: string
  isPlugin?: boolean
  staticMap?: any
  configuration?: {}
}

export interface Field {
  accuracy: number
  originName: string
  fieldSize: number
  fieldType: string
  name: string
  deExtractType: number
  checked: boolean
  primaryKey: boolean
  length: number
}
const props = defineProps({
  param: {
    required: false,
    default() {
      return reactive<{
        id: string
        name: string
        desc: string
        type: string
        editType: number
      }>({
        id: '0',
        name: '',
        desc: '',
        type: 'Excel',
        editType: 0
      })
    },
    type: Object
  },
  isSupportSetKey: {
    type: boolean,
    required: true
  }
})

const { param, isSupportSetKey } = toRefs(props)

const { t } = useI18n()
const { emitter } = useEmitt()

const loading = ref(false)
const columns = shallowRef([])
const multipleSelection = shallowRef([])
const multipleTable = ref()

const defaultSheetObj = {
  tableName: ' ',
  sheetExcelId: '',
  fields: [],
  jsonArray: [],
  nameExist: false,
  empty: '',
  overLength: false
}
const sheetObj = reactive(cloneDeep(defaultSheetObj))
const state = reactive({
  excelData: [],
  defaultExpandedKeys: [],
  defaultCheckedKeys: [],
  fileList: null,
  sheets: []
})

const sheetFile = computed(() => {
  const [sheet = {}] = state.excelData
  return {
    name: sheet.excelLabel,
    size: sheet.excelLabel
  }
})

const uploading = ref(false)

const fieldType = {
  TEXT: 'text',
  DATETIME: 'time',
  LONG: 'value',
  DOUBLE: 'value'
}

const fieldTypeToStr = {
  0: 'TEXT',
  2: 'LONG',
  3: 'DOUBLE'
}

const generateColumns = (arr: Field[]) =>
  arr.map(ele => ({
    key: ele.originName,
    fieldType: ele.fieldType,
    deExtractType: ele.deExtractType,
    dataKey: ele.originName,
    title: ele.name,
    checked: ele.checked,
    primaryKey: ele.primaryKey,
    length: ele.length,
    width: 150,
    headerCellRenderer: ({ column }) => (
      <div class="flex-align-center icon">
        <ElIcon>
          <Icon>
            {h(iconFieldMap[fieldType[column.fieldType]], {
              class: `svg-icon field-icon-${fieldType[column.fieldType]}`
            })}
          </Icon>
        </ElIcon>
        <span class="ellipsis" title={column.title} style={{ width: '100px' }}>
          {column.title}
        </span>
      </div>
    )
  }))

const handleNodeClick = data => {
  if (data.sheet) {
    Object.assign(sheetObj, data)
    columns.value = generateColumns(data.fields)
    multipleSelection.value = columns.value.filter(item => item.checked)
    currentMode.value = 'preview'
  }
}

const beforeUpload = () => {
  uploading.value = true
}

const handleTabClick = tab => {
  activeTab.value = tab.value
  const sheet = state.excelData[0]?.sheets.find(ele => ele.sheetId === tab.value)
  handleNodeClick(sheet)
}

const uploadFail = response => {
  state.excelData = []
  activeTab.value = ''
  tabList.value = []
  Object.assign(sheetObj, cloneDeep(defaultSheetObj))
  let myError = response.toString()
  myError.replace('Error: ', '')
}
const tabList = shallowRef([])
const activeTab = ref('')

const handleExcelDel = () => {
  state.excelData = []
  activeTab.value = ''
  tabList.value = []
  Object.assign(sheetObj, cloneDeep(defaultSheetObj))
}

const uploadSuccess = response => {
  if (!response) {
    return
  }
  if (response?.code !== 0) {
    state.excelData = []
    activeTab.value = ''
    tabList.value = []
    Object.assign(sheetObj, cloneDeep(defaultSheetObj))
    ElMessage.warning(response.msg)
    return
  }
  columns.value = []
  Object.assign(sheetObj, cloneDeep(defaultSheetObj))
  multipleSelection.value = []
  uploading.value = false
  if (!param.value.name) {
    param.value.name = response.data.excelLabel
  }
  tabList.value = response.data.sheets.map(ele => {
    const { sheetId, tableName, newSheet } = ele
    return {
      value: sheetId,
      label: tableName,
      newSheet: newSheet
    }
  })
  state.excelData = [response.data]
  const [sheet] = tabList.value

  sheet && handleTabClick(sheet)
}
const saveExcelDs = (params, successCb, finallyCb) => {
  let validate = true
  let selectedSheet = []
  let sheetFileMd5 = []
  let effectExtField = false
  let changeFiled = false
  let selectNode = state.excelData[0]?.sheets
  for (let i = 0; i < selectNode.length; i++) {
    if (selectNode[i].sheet) {
      if (selectNode[i].effectExtField) {
        effectExtField = true
      }
      if (selectNode[i].changeFiled) {
        changeFiled = true
      }
      if (selectNode[i].fields.filter(field => field.checked).length == 0) {
        ElMessage({
          message: selectNode[i].excelLabel + t('datasource.api_field_not_empty'),
          type: 'error'
        })
        finallyCb?.()
        return
      }
      for (let j = 0; j < selectNode[i].fields.length; j++) {
        if (
          selectNode[i].fields[j].checked &&
          selectNode[i].fields[j].primaryKey &&
          !selectNode[i].fields[j].length &&
          selectNode[i].fields[j].deExtractType === 0
        ) {
          ElMessage({
            message:
              t('datasource.primary_key_length') +
              selectNode[i].excelLabel +
              ': ' +
              selectNode[i].fields[j].name,
            type: 'error'
          })
          finallyCb?.()
          return
        }
      }
      selectedSheet.push(selectNode[i])
      sheetFileMd5.push(selectNode[i].fieldsMd5)
    }
  }
  if (!selectedSheet.length) {
    ElMessage({
      message: t('dataset.ple_select_excel'),
      type: 'error'
    })
    finallyCb?.()
    return
  }
  if (!validate) {
    finallyCb?.()
    return
  }

  let table = {}
  if (params) {
    param.value.name = params.name
  }
  if (!props.param.id) {
    table = {
      id: props.param.id,
      name: props.param.name,
      type: 'Excel',
      sheets: selectedSheet,
      editType: 0
    }
  } else {
    table = {
      id: props.param.id,
      name: props.param.name,
      type: 'Excel',
      sheets: selectedSheet,
      editType: props.param.editType ? props.param.editType : 0
    }
  }

  if (props.param.editType === 0 && props.param.id && (effectExtField || changeFiled)) {
    ElMessageBox.confirm(t('deDataset.replace_the_data'), {
      confirmButtonText: t('dataset.confirm'),
      tip: t('data_source.to_replace_it'),
      cancelButtonText: 'Cancel',
      confirmButtonType: 'primary',
      type: 'warning',
      autofocus: false,
      showClose: false,
      callback: (action: Action) => {
        if (action === 'confirm') {
          saveExcelData(sheetFileMd5, table, params, successCb, finallyCb)
        }
      }
    })
  } else {
    saveExcelData(sheetFileMd5, table, params, successCb, finallyCb)
  }
}

const saveExcelData = (sheetFileMd5, table, params, successCb, finallyCb) => {
  for (let i = 0; i < table.sheets.length; i++) {
    table.sheets[i].data = []
    table.sheets[i].jsonArray = []
  }
  table.configuration = Base64.encode(JSON.stringify(table.sheets))
  let method = save
  if (!table.id || table.id === '0') {
    delete table.id
    table.pid = params.pid
  } else {
    method = update
  }
  if (loading.value) return
  loading.value = true
  method(table)
    .then(res => {
      emitter.emit('showFinishPage', res)
      successCb?.()
      ElMessage({
        message: t('commons.save_success'),
        type: 'success'
      })
    })
    .finally(() => {
      finallyCb?.()
      loading.value = false
    })
}

const onChange = file => {
  state.fileList = file
}
const isResize = ref(true)

const handleResize = debounce(() => {
  isResize.value = false
  nextTick(() => {
    isResize.value = true
    if (currentMode.value === 'select') {
      nextTick(() => {
        initMultipleTable.value = true
        for (let i = 0; i < columns.value.length; i++) {
          if (columns.value[i].checked) {
            multipleTable?.value?.toggleRowSelection(columns.value[i], true)
          }
        }
        initMultipleTable.value = false
      })
    }
  })
}, 500)
onMounted(() => {
  window.addEventListener('resize', handleResize)
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
})

const upload = ref()
const uploadAgain = ref()

const uploadExcel = () => {
  const formData = new FormData()
  formData.append('file', state.fileList.raw)
  formData.append('type', '')
  formData.append('editType', param.value.editType)
  formData.append('id', param.value.id || 0)
  loading.value = true
  return uploadFile(formData)
    .then(res => {
      upload.value?.clearFiles()
      uploadAgain.value?.clearFiles()
      uploadSuccess(res)
      loading.value = false
    })
    .catch(error => {
      state.excelData = []
      activeTab.value = ''
      tabList.value = []
      Object.assign(sheetObj, cloneDeep(defaultSheetObj))
      if (error.code === 'ECONNABORTED') {
        ElMessage({
          type: 'error',
          message: error.message,
          showClose: true
        })
      }
      loading.value = false
    })
}
const excelForm = ref()
const submitForm = () => {
  return excelForm.value.validate
}

const showName = ref(true)

const appendReplaceExcel = response => {
  showName.value = false
  uploadSuccess(response)
}

const status = ref(false)
const initMultipleTable = ref(false)
const currentMode = ref('preview')

const deExtractTypeChange = item => {
  item.deType = item.deExtractType
  const sheet = state.excelData[0]?.sheets.find(ele => ele.sheetId === activeTab.value)
  sheet.fields.forEach(row => {
    if (row.originName === item.dataKey) {
      row.deExtractType = item.deExtractType
      row.deType = item.deExtractType
      row.fieldType = fieldTypeToStr[item.deExtractType]
    }
  })
}

const lengthChange = val => {
  const sheet = state.excelData[0]?.sheets.find(ele => ele.sheetId === activeTab.value)
  sheet.fields.forEach(row => {
    if (row.originName === val.dataKey) {
      row.length = val.length
    }
  })
}
const primaryKeyChange = val => {
  const sheet = state.excelData[0]?.sheets.find(ele => ele.sheetId === activeTab.value)
  sheet.fields.forEach(row => {
    if (row.originName === val.dataKey) {
      row.primaryKey = val.primaryKey
    }
  })
}

const fieldOptions = [
  { label: t('dataset.text'), value: 0 },
  { label: t('dataset.value'), value: 2 },
  {
    label: t('dataset.value') + '(' + t('dataset.float') + ')',
    value: 3
  }
]

const handleSelectionChange = val => {
  if (!initMultipleTable.value) {
    multipleSelection.value = val
    multipleSelection.value.forEach(row => {
      row.checked = true
    })
    columns.value.forEach(row => {
      let item
      for (let i = 0; i < multipleSelection.value.length; i++) {
        if (row.dataKey === multipleSelection.value[i].dataKey) {
          item = multipleSelection.value[i]
        }
      }
      if (item) {
        row.checked = item.checked
      } else {
        row.checked = false
      }
    })

    const sheet = state.excelData[0]?.sheets.find(ele => ele.sheetId === activeTab.value)
    sheet.fields.forEach(row => {
      let item
      for (let i = 0; i < multipleSelection.value.length; i++) {
        if (row.originName === multipleSelection.value[i].dataKey) {
          item = multipleSelection.value[i]
        }
      }
      if (item) {
        row.checked = item.checked
      } else {
        row.checked = false
      }
    })
  }
}

const disabledFieldLength = item => {
  if (!item.checked) {
    return true
  }
  if (item.deExtractType !== 0) {
    return true
  }
}

const changeCurrentMode = val => {
  currentMode.value = val
  if (val === 'select') {
    nextTick(() => {
      initMultipleTable.value = true
      for (let i = 0; i < columns.value.length; i++) {
        if (columns.value[i].checked) {
          multipleTable?.value?.toggleRowSelection(columns.value[i], true)
        }
      }
      initMultipleTable.value = false
    })
  } else {
    const sheet = state.excelData[0]?.sheets.find(ele => ele.sheetId === activeTab.value)
    handleNodeClick(sheet)
  }
}

const uploadStatus = val => {
  status.value = val
}
defineExpose({
  saveExcelDs,
  submitForm,
  sheetFile,
  appendReplaceExcel,
  uploadStatus
})
</script>

<template>
  <div class="excel-detail">
    <div class="detail-inner">
      <el-form
        ref="excelForm"
        require-asterisk-position="right"
        :model="param"
        label-position="top"
        v-loading="loading"
      >
        <el-form-item
          v-if="sheetFile.name"
          prop="id"
          :label="t('data_source.document')"
          key="sheetFile"
          :rules="[
            {
              required: true
            }
          ]"
        >
          <ExcelInfo
            @del="handleExcelDel"
            show-del
            :name="sheetFile.name"
            :size="sheetFile.size"
          ></ExcelInfo>
          <el-upload
            action=""
            :multiple="false"
            ref="uploadAgain"
            :show-file-list="false"
            accept=".xls,.xlsx,.csv"
            :before-upload="beforeUpload"
            :on-change="onChange"
            :http-request="uploadExcel"
            :on-error="uploadFail"
            name="file"
          >
            <template #trigger>
              <el-button text>{{ t('data_source.reupload') }}</el-button>
            </template>
          </el-upload>
        </el-form-item>
        <el-form-item
          v-else
          prop="id"
          key="sheetId"
          :label="t('data_source.document')"
          :rules="[
            {
              required: true
            }
          ]"
        >
          <el-upload
            :multiple="false"
            action=""
            ref="upload"
            :show-file-list="false"
            accept=".xls,.xlsx,.csv"
            :before-upload="beforeUpload"
            :on-change="onChange"
            :http-request="uploadExcel"
            :on-error="uploadFail"
            name="file"
          >
            <template #trigger>
              <el-button secondary>
                <template #icon>
                  <Icon name="icon_upload_outlined"><icon_upload_outlined class="svg-icon" /></Icon>
                </template>
                {{ t('dataset.upload_file') }}
              </el-button>
            </template>
          </el-upload>
          <p class="upload-tip" style="width: 100%">{{ t('data_source.and_csv_formats') }}</p>
          <div class="ed-form-item__error" v-if="status">
            {{ t('data_source.please_upload_files') }}
          </div>
        </el-form-item>
        <el-form-item
          :class="status && !sheetFile.name && 'error-status'"
          prop="name"
          key="name"
          v-if="showName"
          :rules="[
            {
              required: true,
              message:
                t('common.please_input') +
                t('common.empty') +
                t('datasource.datasource') +
                t('common.empty') +
                t('common.name')
            }
          ]"
          :label="
            t('visualization.custom') +
            t('common.empty') +
            t('datasource.datasource') +
            t('common.empty') +
            t('common.name')
          "
        >
          <el-input
            v-model="param.name"
            :placeholder="
              t('common.please_input') +
              t('common.empty') +
              t('datasource.datasource') +
              t('common.empty') +
              t('common.name')
            "
          />
        </el-form-item>
      </el-form>
      <template v-if="activeTab">
        <div class="title-form_primary">
          {{ t('chart.data_preview') }}
        </div>
        <SheetTabs
          :activeTab="activeTab"
          @tab-click="handleTabClick"
          :tab-list="tabList"
        ></SheetTabs>

        <div class="table-select_mode" v-if="param.editType === 0">
          <div class="btn-select">
            <el-button
              @click="changeCurrentMode('preview')"
              :class="[currentMode === 'preview' && 'is-active']"
              text
            >
              {{ t('chart.data_preview') }}
            </el-button>
            <el-button
              @click="changeCurrentMode('select')"
              :class="[currentMode === 'select' && 'is-active']"
              text
            >
              {{ t('data_set.field_selection') }}
            </el-button>
          </div>
        </div>
        <div
          class="info-table"
          :class="param.editType === 0 && 'info-table_height'"
          v-if="isResize"
        >
          <el-auto-resizer v-if="currentMode === 'preview'">
            <template #default="{ height, width }">
              <el-table-v2
                :columns="multipleSelection"
                header-class="excel-header-cell"
                :data="sheetObj.jsonArray"
                :width="width"
                :height="height"
                fixed
              />
            </template>
          </el-auto-resizer>
          <el-table
            header-class="header-cell"
            v-else
            ref="multipleTable"
            :data="columns"
            style="width: 100%"
            @selection-change="handleSelectionChange"
          >
            <el-table-column type="selection" width="55" />
            <el-table-column :label="t('data_set.field_name')">
              <template #default="scope">{{ scope.row.title }}</template>
            </el-table-column>

            <el-table-column prop="deExtractType" :label="t('datasource.field_type')">
              <template #default="scope">
                <el-select
                  v-model="scope.row.deExtractType"
                  class="select-type"
                  style="display: inline-block; width: 120px"
                  @change="deExtractTypeChange(scope.row)"
                >
                  <template #prefix>
                    <el-icon>
                      <Icon :className="`field-icon-${fieldTypeLowercase[scope.row.deExtractType]}`"
                        ><component
                          class="svg-icon"
                          :class="`field-icon-${fieldTypeLowercase[scope.row.deExtractType]}`"
                          :is="iconFieldMap[fieldTypeLowercase[scope.row.deExtractType]]"
                        ></component
                      ></Icon>
                    </el-icon>
                  </template>
                  <el-option
                    v-for="item in fieldOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  >
                    <span style="float: left">
                      <el-icon>
                        <Icon :className="`field-icon-${fieldTypeLowercase[item.value]}`"
                          ><component
                            class="svg-icon"
                            :class="`field-icon-${fieldTypeLowercase[item.value]}`"
                            :is="iconFieldMap[fieldTypeLowercase[item.value]]"
                          ></component
                        ></Icon>
                      </el-icon>
                    </span>
                    <span style="float: left; font-size: 12px; color: #8492a6">{{
                      item.label
                    }}</span>
                  </el-option>
                </el-select>
              </template>
            </el-table-column>
            <el-table-column
              prop="length"
              :label="t('datasource.length')"
              v-if="param.editType === 0"
            >
              <template #default="scope">
                <el-input-number
                  :disabled="disabledFieldLength(scope.row)"
                  v-model="scope.row.length"
                  autocomplete="off"
                  step-strictly
                  class="text-left edit-all-line"
                  :min="1"
                  :max="512"
                  :placeholder="t('common.inputText')"
                  controls-position="right"
                  type="number"
                  @change="lengthChange(scope.row)"
                />
              </template>
            </el-table-column>
            <el-table-column
              prop="primaryKey"
              class-name="checkbox-table"
              :label="t('datasource.set_key')"
              width="100"
              v-if="param.editType === 0 && isSupportSetKey"
            >
              <template #default="scope">
                <el-checkbox
                  :key="scope.row.dataKey"
                  v-model="scope.row.primaryKey"
                  :disabled="!scope.row.checked"
                  @change="primaryKeyChange(scope.row)"
                >
                </el-checkbox>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </template>
    </div>
  </div>
</template>

<style lang="less">
.excel-detail {
  display: flex;
  justify-content: center;
  width: calc(100% + 48px);
  margin: -8px -24px 0 -24px;
  .ed-form-item {
    margin-bottom: 16px;
  }

  .table-select_mode {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: #f5f6f7;
    padding: 16px;
    .btn-select {
      min-width: 164px;
      padding: 0 6px;
      height: 32px;
      display: flex;
      align-items: center;
      justify-content: center;
      background: #ffffff;
      border: 1px solid #bbbfc4;
      border-radius: 4px;

      .is-active {
        background: var(--ed-color-primary-1a, rgba(51, 112, 255, 0.1));
      }

      .ed-button:not(.is-active) {
        color: #1f2329;
      }
      .ed-button.is-text {
        height: 24px;
        min-width: 74px;
        line-height: 24px;
      }
      .ed-button + .ed-button {
        margin-left: 4px;
      }
    }
  }

  .detail-operate {
    height: 56px;
    padding: 16px 24px;
    font-size: 16px;
    font-weight: 500;
    width: 100%;
    border-bottom: 1px solid rgba(31, 35, 41, 0.15);
  }
  .detail-inner {
    width: 800px;
    padding-top: 16px;
    height: calc(100vh - 280px);
    min-height: 700px;

    .dropdown-icon {
      .down-outlined {
        transform: rotate(180deg);
      }
      &[aria-expanded='true'] {
        .down-outlined {
          transform: rotate(0);
        }
      }
      cursor: pointer;
    }

    .error-status {
      margin-top: 32px;
    }

    .upload-tip {
      color: #8f959e;
      font-family: var(--de-custom_font, 'PingFang');
      font-size: 14px;
      font-style: normal;
      font-weight: 400;
      line-height: 22px;
    }

    .title-form_primary {
      margin: 16px 0;
      margin-top: 32px;
    }

    .info-table {
      width: 100%;
      height: calc(100% - 200px);
      .ed-select--light .ed-select__prefix:after {
        display: none;
      }
      &.info-table_height {
        height: calc(100% - 379px);
      }
    }
  }
}
</style>
